package com.gzist.blog_system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gzist.blog_system.domain.User;
import com.gzist.blog_system.domain.result.Result;
import com.gzist.blog_system.domain.vo.LoginParams;
import com.gzist.blog_system.domain.vo.UserInfoParams;
import com.gzist.blog_system.mapper.UserMapper;
import com.gzist.blog_system.service.UserService;
import com.gzist.blog_system.util.JWTUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthenticationManager manager;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public String login(LoginParams loginParams) {
        log.info("进入到userServiceImpl后的账号：{}", loginParams.getUsername() + loginParams.getPassword());
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginParams.getUsername(), loginParams.getPassword());
        Authentication authentication = null;
        try {
            //登录成功后将其用户信息取出存进authentication中
            authentication = manager.authenticate(authenticationToken);
            log.info("有没有：{}", authentication);
        } catch (AuthenticationException e) {
            //当系统捕捉到错误的时候就
            e.printStackTrace();
            log.info("系统捕捉到账号密码错误时候的账号：{}", loginParams.getUsername() + loginParams.getPassword());
            throw new BadCredentialsException("账号或者密码错误！");
        }
        //拿到用户信息后将其与权限信息一齐封装存进jwt中进行返回
        Map<String, Object> claims = new HashMap<>();
        User user = (User) authentication.getPrincipal();
        // 对于登录业务这只需要将用户信息存进authentication即可，无需存进SecurityContextHolder
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());
        claims.put("isAdmin", user.getIsAdmin());
        claims.put("perms", user.getPerms());
        //拿到token后同时将其存金redis中(过期时间为一天，跟token的过期时间一致)
        String token = JWTUtils.createJwt(claims);
        stringRedisTemplate.opsForValue().set(token, token, 1, TimeUnit.DAYS);
        return token;
    }

    @Override
    public UserInfoParams userInfo() {
        //有个疑问，就是在这能够用上下文获取用户信息，但是在下面的key获取的时候却获取不到
        UserInfoParams userInfo = new UserInfoParams();
        // 从上下文获取到用户信息然后将其封装后返回
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) authentication.getPrincipal();
        log.info("在获取用户信息的时候的authentication信息：{}",authentication);
        userInfo.setId(user.getId());
        userInfo.setNickname(user.getNickname());
        userInfo.setEmail(user.getEmail());
        userInfo.setCreateTime(user.getCreateTime());
        userInfo.setUsername(user.getUsername());
        userInfo.setUserPic(user.getUserPic());
        return userInfo;
    }

    @Override
    public boolean updateUserInfo(UserInfoParams userInfoParams) {
        return userMapper.updateUserInfo(userInfoParams);
    }

    @Override
    public boolean updateAvatar(String userPic) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) authentication.getPrincipal();
        log.info("打印一下在获取用户信息的时候的用户信息：{}",authentication.getPrincipal());
        Long id = user.getId();
        return userMapper.updateAvatar(id, userPic);
    }

    @Override
    public String updatePwd(Map<String, String> params, String token) {
        //首先先做参数得校验
        String oldPwd = params.get("old_pwd");
        String newPwd = params.get("new_pwd");
        String rePwd = params.get("re_pwd");
        for (String pwd : params.values()) {
            if (!StringUtils.hasLength(pwd)) {
                return "参数不符合标准！！！";
            }
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User s = (User) authentication.getPrincipal();
        String username = s.getUsername();
        Long id = s.getId();
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
        // 将输入的密码与查询出来的密码的旧密码进行匹配
        if (!passwordEncoder.matches(oldPwd,user.getPassword())) {
            return "旧密码输入错误！！!";
        }
        if (!newPwd.equals(rePwd)) {
            return "两次输出得新密码不一致！！!";
        }
        //输入验证通过后将其密码进行加密
        String enNewPwd = passwordEncoder.encode(newPwd);
        //然后进行更改密码操作和返回结果
        userMapper.updatePwd(enNewPwd, id);
        //在成功修改密码后将redis中的令牌删去
        stringRedisTemplate.opsForValue().getOperations().delete(token);
        return "success";
    }

    @Override
    public String userKey(String token) {
        //因为根据上下文路径的方式拿不到用户信息，因此在这根据token来拿
        Claims claims = JWTUtils.parseJwt(token);
        Boolean isAdmin =  (Boolean) claims.get("isAdmin");
        String result = String.valueOf(isAdmin);
        log.info("打印下访问用户密钥时候的用户信息：{}",result);
        return result;
    }

    @Override
    public void register(String username, String password) {
        //注册记得将密码给加密后再将其存进数据库
        String encodePwd = passwordEncoder.encode(password);
        userMapper.register(username,encodePwd);
    }

}




